package com.yh.csx.bsf.cat;

import com.google.common.util.concurrent.RateLimiter;
import com.yh.csx.bsf.cat.util.UrlParser;
import com.yh.csx.bsf.core.util.LogUtils;
import com.yh.csx.bsf.core.util.PropertyUtils;
import com.yh.csx.bsf.core.util.WebUtils;
import lombok.val;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @创建人 霍钧城
 * @创建时间 2021年05月13日 13:57:00
 * @描述
 */
public class CatRateLimiter {
    public static final String Project="RateLimiter";
    private static final Map<String, Double> resourceQpsCache = new ConcurrentHashMap<String, Double>(256);
    private static final Map<String, RateLimiter> resourceRateLimiterCache = new ConcurrentHashMap<String, RateLimiter>(256);

    public static void updateResourceQps(String resource, double qps) {
        resourceQpsCache.put(resource, qps);
    }

    public static void updateResourceQpsAll(Map map) {
        resourceQpsCache.clear();
        resourceQpsCache.putAll(map);
        if(PropertyUtils.getPropertyCache(CatProperties.RateLimiterLoggerEnabled,false)){
            resourceQpsCache.forEach((k,v)->LogUtils.info(CatRateLimiter.class, Project,"K:"+k+" V:"+v));
        }
    }

    public static void removeResource(String resource) {
        resourceQpsCache.remove(resource);
    }

    public static boolean enter(String type,String requestURI) {
        String resource=CatProperties.getRateLimiterCatName()+"-"+type+"-"+ UrlParser.format(requestURI);
        long t1 = System.currentTimeMillis();
        AtomicReference<Double> qps = new AtomicReference<>(0.0);
        Optional.ofNullable(resourceQpsCache.get(resource)).ifPresent(v-> qps.set(v));
        if (qps.get() == 0.0) {return true;}
        RateLimiter rateLimiter = resourceRateLimiterCache.get(resource);
        //if null,new limiter
        if (rateLimiter == null || qps.get()!=rateLimiter.getRate()) {
            rateLimiter = RateLimiter.create(qps.get());
            RateLimiter putByOtherThread = resourceRateLimiterCache.put(resource, rateLimiter);
            if (putByOtherThread != null) {
                rateLimiter = putByOtherThread;
            }
            rateLimiter.setRate(qps.get());
        }
        if (!rateLimiter.tryAcquire()) {
            if(PropertyUtils.getPropertyCache(CatProperties.RateLimiterLoggerEnabled,false)){
                LogUtils.warn(CatRateLimiter.class, Project, "FAIL:" + (System.currentTimeMillis() - t1) + "ms;" +"Req：" + resource);
            }
            return false;
        } else {
            if(PropertyUtils.getPropertyCache(CatProperties.RateLimiterLoggerEnabled,false)){
                LogUtils.info(CatRateLimiter.class, Project, "SUCCESS:" + (System.currentTimeMillis() - t1) + "ms;" +"Req：" + resource);
            }
            return true;
        }
    }

    public static void responseResult() {
        try {
            val response = WebUtils.getResponse();
            response.reset();
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Content-Type", "application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().append("{\"code\":600000,\"message\":\"系统流控，请稍后重试！\",\"data\":null}");
            response.getWriter().flush();
            response.getWriter().close();
        } catch (Exception e) {
        }
    }
}
